/*
 *	Copyright (c) 2010, tn
 *	All rights reserved.
 *
 *	Redistribution and use in source and binary forms, with or without
 *	modification, are permitted provided that the following conditions are met:
 *	    * Redistributions of source code must retain the above copyright
 *	      notice, this list of conditions and the following disclaimer.
 *	    * Redistributions in binary form must reproduce the above copyright
 *	      notice, this list of conditions and the following disclaimer in the
 *	      documentation and/or other materials provided with the distribution.
 *	    * Neither the name of the <organization> nor the
 *	      names of its contributors may be used to endorse or promote products
 *	      derived from this software without specific prior written permission.
 *
 *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *	DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 *	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "mcunit.h"

#include <stdlib.h>
#include <stdio.h>

const char *mcunit_suite_indent = "";
const char *mcunit_case_indent  = "\t";
const char *mcunit_test_indent  = "\t\t";

int mcunit_test_run(mcunit_test_s *test) {
	/* Check function pointer. If NULL, we have reached end-of-list */
	if( test->test != NULL ) {
		MCUNIT_PRINTF("%stest: %s\n", mcunit_test_indent, test->name);
		test->result = test->test(test);
		return test->result;
	} else {
		test->result = mcunit_no_test;
		return test->result;
	}
}

int mcunit_case_run(mcunit_case_s *test_case) {
	int result, index;
	MCUNIT_PRINTF("%scase: %s\n", mcunit_case_indent, test_case->name);

	test_case->passed = 0;
	test_case->runs   = 0;
	index = 0;

	/*
	 * 	Run tests until end of list. eol is reported by test runner.
	 */
	while( (result = mcunit_test_run( &test_case->tests[index])) != mcunit_no_test) {
		if( result == 0 ) test_case->passed++;
		test_case->runs++;
		index++;
	}

	return (test_case->runs - test_case->passed);
}

int mcunit_suite_run(mcunit_suite_s *test_suite) {
	int index = 0;
	test_suite->passed = 0;
	test_suite->runs   = 0;

	MCUNIT_PRINTF("%ssuite: %s\n", mcunit_suite_indent, test_suite->name);

	/*
	 * Iterate over cases list until NULL is found. Invoke case runner
	 * for every case.
	 */
	while( test_suite->cases[index] != NULL ) {
		if( mcunit_case_run( test_suite->cases[index] ) == 0 ) {
			test_suite->passed++;
		}
		test_suite->runs++;
		index++;
	}
	return (test_suite->runs - test_suite->passed); /* All should pass */
}

void mcunit_test_report(mcunit_test_s *test) {
	if( test->result == 0 ) {
		MCUNIT_PRINTF("PASSED: %s\n", test->name);
	} else {
		MCUNIT_PRINTF("FAILED (%d): %s", test->result, test->name);
		if( test->msg ) MCUNIT_PRINTF(": %s\n", test->msg);
		else            MCUNIT_PRINTF("\n");
	}
}

void mcunit_case_report(mcunit_case_s *test_case, int depth) {
	int index = 0;
	MCUNIT_PRINTF("%s: passed %d/%d tests\n", test_case->name, test_case->passed, test_case->runs);

	if( depth <= 0 ) return;
	while( test_case->tests[index].test != NULL ) {
		MCUNIT_PRINTF("\t");
		mcunit_test_report(&test_case->tests[index]);
		index++;
	}
}

void mcunit_suite_report(mcunit_suite_s *test_suite, int depth) {
	mcunit_case_s *test_case;

	int ci = 0;
	int ti = 0;

	MCUNIT_PRINTF("%s: passed %d/%d cases\n", test_suite->name, test_suite->passed, test_suite->runs);
	if( depth <= 0 ) return;
	while( (test_case = test_suite->cases[ci]) != NULL ) {
		MCUNIT_PRINTF("\t%s: passed %d/%d tests\n", test_case->name, test_case->passed, test_case->runs);
		ci++;
		if( depth <= 1 ) continue;
		while( test_case->tests[ti].test != NULL ) {
			printf("\t\t");
			mcunit_test_report(&test_case->tests[ti]);
			ti++;
		}
	}
}
